package com.thoughtorigin.flux.asflux {
import com.thoughtorigin.flux.*;

import com.thoughtorigin.util.ClassCreator;

import flash.utils.Dictionary;

public class ActionscriptFlux implements Flux {

    private var _settings: ActionscriptFluxSettings;
    private var _objDictionary: Dictionary;

    public function ActionscriptFlux() {
        _objDictionary = new Dictionary();
    }

    public function set settings(value: ActionscriptFluxSettings): void {
        _settings = value;        
    }

    public function getProperty(name:String):Object {
        var objDef: ObjectDef = _settings.getObjectDef(name);
        if(objDef && objDef.createNew) {
            return createNew(name);
        }
        if(_objDictionary[name]) {
            return _objDictionary[name];
        }

        var obj: Object = createInstanceByName(name, objDef);
        assignProperties(obj, objDef);
        executeFunctions(obj, objDef);
        return obj;
    }

    public function createNew(name:String):Object {
        var objDef: ObjectDef = _settings.getObjectDef(name);
        var retVal: Object = createInstanceByName(name, objDef);
        assignProperties(retVal, objDef);
        executeFunctions(retVal, objDef);
        return retVal;
    }

    private function createInstanceByName(name: String, objDef: ObjectDef): Object {
        if(!objDef) {
            throw new PropertyNotFoundException(name + " was not found in IOC.");
        }
        var clazz: Class = objDef.getClass();
        var obj: Object = createInstance(clazz, objDef.constructorArgs);
        if(!_objDictionary[name]) {
            _objDictionary[name] = obj;            
        }
        return obj;
    }

    public function getClass(name:String):Class {
        var retVal: Class = _settings.getObjectDef(name).getClass();
        if(!retVal) {
            throw new ClassDefinitionNotFoundException(name + " was not found in applicationDomain.")    
        }
        return retVal;
    }

    private function assignProperties(obj: Object, objDef: ObjectDef): void {
        for each(var property: Property in objDef.properties) {
            obj[property.propertyName] = property.value;
        }
    }

    private function executeFunctions(obj: Object, objDef: ObjectDef): void {
        for each(var execution: Execution in objDef.executions) {
            var func: Function = obj[execution.functionName];
            func.apply(this, getArgValues(execution.args));
        }
    }

    private function getArgValues(args: Array): Array {
        var retVal: Array = [];
        for each(var arg: Argument in args) {
            retVal.push(arg.value);
        }
        return retVal;
    }

    /**
     * creates objects up to 9 args
     *
     * @param args
     * @return
     */
    protected function createInstance(clazz: Class, args: Array): Object {
        var tmpArgs: Array = [];
        for each(var arg: Argument in args) {
            tmpArgs.push(arg.value);
        }
        return ClassCreator.create(clazz, tmpArgs);
    }

}
}